`timescale 1ns / 10ps

// Copyright 2023 Sycuricon Group
// Author: Jinyan Xu (phantom@zju.edu.cn)
`include "CSRStruct.vh"
`include "RegStruct.vh"
`include "DDRStruct.vh"
`include "MMUStruct.vh"

module Testbench;

    reg clk = 1'b0;
    reg rstn = 1'b0;

    initial begin
        #20;
        rstn = 1'b1;
    end
    always begin
        #5;
        clk = ~clk;
    end

    wire [63:0] cosim_pc;
    wire [31:0] cosim_inst;
    wire [ 7:0] cosim_rs1_id;
    wire [63:0] cosim_rs1_data;
    wire [ 7:0] cosim_rs2_id;
    wire [63:0] cosim_rs2_data;
    wire [63:0] cosim_alu;
    wire [63:0] cosim_mem_addr;
    wire [ 3:0] cosim_mem_we;
    wire [63:0] cosim_mem_wdata;
    wire [63:0] cosim_mem_rdata;
    wire [ 3:0] cosim_rd_we;
    wire [ 7:0] cosim_rd_id;
    wire [63:0] cosim_rd_data;
    wire [ 3:0] cosim_br_taken;
    wire [63:0] cosim_npc;
    wire [63:0] cosim_disp;
    wire [63:0] cosim_mtime;
    wire [63:0] cosim_mtimecmp;

    wire        cosim_valid;
    wire        cosim_mmio_store;
    wire [63:0] cosim_mmio_len;
    wire [63:0] cosim_mmio_val;
    wire [63:0] cosim_mmio_addr;
    wire        cosim_interrupt;
    wire [63:0] cosim_cause;

    CSRStruct::CSRPack cosim_csr_info;
    RegStruct::RegPack cosim_regs;

    DDR_ift ddr_request ();
    DDRStruct::DDRDebugCorePack ddr_debug_core;
    Uart_ift uart_ift ();
    MMUStruct::MMUPack cosim_mmu_info;

    PipelineCPU dut (
        .clk (clk),
        .rstn(rstn),

        .cosim_pc       (cosim_pc),
        .cosim_inst     (cosim_inst),
        .cosim_rs1_id   (cosim_rs1_id),
        .cosim_rs1_data (cosim_rs1_data),
        .cosim_rs2_id   (cosim_rs2_id),
        .cosim_rs2_data (cosim_rs2_data),
        .cosim_alu      (cosim_alu),
        .cosim_mem_addr (cosim_mem_addr),
        .cosim_mem_we   (cosim_mem_we),
        .cosim_mem_wdata(cosim_mem_wdata),
        .cosim_mem_rdata(cosim_mem_rdata),
        .cosim_rd_we    (cosim_rd_we),
        .cosim_rd_id    (cosim_rd_id),
        .cosim_rd_data  (cosim_rd_data),
        .cosim_br_taken (cosim_br_taken),
        .cosim_npc      (cosim_npc),
        .cosim_csr_info (cosim_csr_info),
        .cosim_regs     (cosim_regs),

        .cosim_disp    (cosim_disp),
        .cosim_mtime   (cosim_mtime),
        .cosim_mtimecmp(cosim_mtimecmp),
        .cosim_mmu_info(cosim_mmu_info),

        .cosim_valid     (cosim_valid),
        .cosim_mmio_store(cosim_mmio_store),
        .cosim_mmio_len  (cosim_mmio_len),
        .cosim_mmio_val  (cosim_mmio_val),
        .cosim_mmio_addr (cosim_mmio_addr),
        .cosim_interrupt (cosim_interrupt),
        .cosim_cause     (cosim_cause),

        .ddr_request   (ddr_request.Master),
        .uart_ift      (uart_ift.Master),
        .ddr_debug_core(ddr_debug_core)
    );

    assign ddr_request.rdata_mem  = 128'b0;
    assign ddr_request.rvalid_mem = 1'b0;
    assign ddr_request.wvalid_mem = 1'b0;
    assign uart_ift.rdata_mem     = 64'b0;
    assign uart_ift.rvalid_mem    = 1'b0;
    assign uart_ift.wvalid_mem    = 1'b0;

`ifdef VERILATE
    wire error;
    cj_comsimulation difftest (
        .clk             (clk),
        .rstn            (rstn),
        .cosim_valid     (cosim_valid),
        .cosim_pc        (cosim_pc),
        .cosim_inst      (cosim_inst),
        .cosim_we        (cosim_rd_we[0]),
        .cosim_rd        (cosim_rd_id[4:0]),
        .cosim_wdate     (cosim_rd_data),
        .cosim_mmio_store(cosim_mmio_store),
        .cosim_mmio_len  (cosim_mmio_len),
        .cosim_mmio_val  (cosim_mmio_val),
        .cosim_mmio_addr (cosim_mmio_addr),
        .cosim_interrupt (cosim_interrupt),
        .cosim_cause     (cosim_cause),
        .error           (error)
    );

    initial begin
        $dumpfile({`TOP_DIR, "/Testbench.vcd"});
        $dumpvars(0, dut);
        $dumpon;
    end
    reg [31:0] instnum =32'b0;
    reg [31:0] cnt = 32'b0;
    reg [31:0] max_cycles = 32'd10000000;
    always @(negedge clk) begin
        cnt <= cnt + 32'b1;
        if(cosim_valid) instnum <= instnum + 32'b1;
        if (error) begin
            $display("[CJ] something error");
            $display("[CJ] Cycles:%d",cnt);
            $display("[CJ] Instrucions:%d",instnum);
            $display("[CJ] CPI: %f", cnt*1.0/instnum);
            $dumpoff;
            $finish;
        end else if (cnt == max_cycles) begin
            $display("[CJ] no simulation time");
            $display("[CJ] Cycles:%d",cnt);
            $display("[CJ] Instrucions:%d",instnum);
            $display("[CJ] CPI: %f", cnt*1.0/instnum);
            $dumpoff;
            $finish;
        end
    end
`endif
endmodule

